#include "MultiplayerGameServer.h"

string MultiplayerGameServer::DEFAULT_PORT = "8080";

MultiplayerGameServer::MultiplayerGameServer(string sceneName)
{
	port = DEFAULT_PORT;
    isRunning = false;
	sessionId = 0;
	sceneState = new SceneState(sceneName);
	sceneState->loadSceneBox();
	sessionUpdateLock = new CriticalSection();
}

MultiplayerGameServer::MultiplayerGameServer(string sceneName, string port)
{
	this->port = port;
    isRunning = false;
	sessionId = 0;
	sceneState = new SceneState(sceneName);
	sceneState->loadSceneBox();
	sessionUpdateLock = new CriticalSection();
}

MultiplayerGameServer::~MultiplayerGameServer()
{
	SessionHandler* session;
	list<SessionHandler*>::iterator it;
	for (it=sessions.begin(); it!=sessions.end(); it++)
	{
		session = *it;
		GameEvent* ge = new GameEvent();
		ge->setCode(8);
		ge->setId(session->getId());
		session->update(ge);
		Sleep(200);
		closesocket(session->getSocket());
		delete session;
	}
	socket->doClose();
	delete socket;
	delete sceneState;
	delete sessionUpdateLock;
}

int MultiplayerGameServer::start(void)
{
	socket = Winsock2SocketFactory::getInstance()->createServerSocket(atoi(port.c_str()));
	socket->setOption(TCP, NO_DELAY);
	Log::getInstance()->info(socket->toString());
	try
	{
		socket->doBind();
		socket->doListen();
		isRunning = true;

		SOCKET clientSocket = INVALID_SOCKET;
		while (isRunning)
		{
			clientSocket = socket->doAccept();
			if (clientSocket == INVALID_SOCKET)
			{
				cout << "accept failed with error: " << WSAGetLastError() << endl;
				return 1;
			}
			else
			{
				cout << "Client socket accepted; its id is " << clientSocket << "." << endl;

				SessionHandler* sh = new SessionHandler(++sessionId, clientSocket, this);
				sessions.push_back(sh);
 
				Thread* sessionThread = ThreadPool::getInstance()->execute(sh);
				sessionThread->join(3000);
			}
		}
		socket->doClose();
	}
	catch (NetworkException& ne)
	{
		Log::getInstance()->error(ne.getMessage());
	}
	delete socket;
    return 0;
}

void MultiplayerGameServer::shutdown(void)
{
	isRunning = false;
}

void MultiplayerGameServer::update(GameEvent* gameEvent)
{
	SessionHandler* session;

	sessionUpdateLock->adquire();
	list<SessionHandler*>::iterator it;
	for (it=sessions.begin(); it!=sessions.end(); it++)
	{
		session = *it;
		int idCompare;
		if (gameEvent->getId() > 1000)
		{
			idCompare=(gameEvent->getId()/1000);
		}
		else idCompare = gameEvent->getId();
		if ((session->getId() != idCompare) && !session->isInvalidated())
		{
			cout << "Forwarding event from session " << sessionId << " to session " << session->getId() << "." << endl;
			session->update(gameEvent);
		}
	}
	sessionUpdateLock->release();
}

DWORD MultiplayerGameServer::run(void)
{
	return start();
}

SceneObjectState* MultiplayerGameServer::createSceneObjectState(GameEvent* gameEvent)
{
	SceneObjectState* state = new SceneObjectState("");
	switch (gameEvent->getCode())
	{
	case 1:
		{//Movimiento 
			state->setX(gameEvent->getX());
			state->setY(gameEvent->getY());
			state->setAngle(gameEvent->getAngle());
			state->setObjName(gameEvent->getName());
			state->setType(TANK);
			state->setId(gameEvent->getId());		   
		}
		break;
	case 2:
		{//Disparo de bala
			state->setX(gameEvent->getX());
			state->setY(gameEvent->getY());
			state->setAngle(gameEvent->getAngle());
			state->setObjName(gameEvent->getSource());
			state->setType(BULLET);
			state->setId(gameEvent->getId());		   
		}
		break;
	case 3:
		{//Muerte de bala
			state->setX(gameEvent->getX());
			state->setY(gameEvent->getY());
			state->setAngle(gameEvent->getAngle());
			state->setObjName(gameEvent->getSource());
			state->setType(BULLET);
			state->setId(gameEvent->getId());		   
		}
		break;
	case 4:
		{//Tanque desconectado
			state->setX(gameEvent->getX());
			state->setY(gameEvent->getY());
			state->setAngle(gameEvent->getAngle());
			state->setObjName(gameEvent->getSource());
			state->setType(TANK);
			state->setId(gameEvent->getId());		   
		}
		break;
	case 5:
		{//Nuevo tanque
			state->setX(gameEvent->getX());
			state->setY(gameEvent->getY());
			state->setAngle(gameEvent->getAngle());
			state->setObjName(gameEvent->getName());
			state->setType(TANK);
			state->setId(gameEvent->getId());		   
		}
		break;
	case 6:
		{//Movimiento bala
			state->setX(gameEvent->getX());
			state->setY(gameEvent->getY());
			state->setAngle(gameEvent->getAngle());
			state->setObjName(gameEvent->getSource());
			state->setType(BULLET);
			state->setId(gameEvent->getId());		   
		}
		break;
	default:break;
	}
	return state;
}

void MultiplayerGameServer::updateSceneState(GameEvent* e)
{
	SceneObjectState* state = new SceneObjectState();
	switch(e->getCode())
	{
	case 1:
		{//Movimiento
			sceneState->updateState(createSceneObjectState(e));
		}
		break;
	case 2:
		{//Disparo de bala
			sceneState->updateNewBullet(createSceneObjectState(e));
		}
		break;
	case 3:
		{//Muerte de bala
			sceneState->updateBulletDeath(createSceneObjectState(e));
		}
		break;
	case 4:
		{//Tanque desconectado
			sceneState->updateTankOff(createSceneObjectState(e));
		}
		break;
	case 5:
		{//Nuevo tanque
			sceneState->updateNewTank(createSceneObjectState(e));
		}
		break;
	default:break;
	}
	this->update(e);
}

SceneState* MultiplayerGameServer::getSceneState()
{
	return sceneState;
}